/**
 * @file defaultLogger.c
 * @brief Realizza una prima implementazione di logger.
 *
 * Sono implementate delle funzioni per gestire la scrittura formattata dei messaggi di log
 * su file concorrenzialmente.
 *
 * @date Apr 11, 2012
 * @author Francesco Corvino
 */
#include "logproxy.h"
#include <stdio.h>
#include <ctype.h>
#include <time.h>
#include <pthread.h>

/**
 * @brief Mutex necessario per proteggere le scritture sul file di log.
 */
static pthread_mutex_t writefcont;

/**
 * @brief Puntatore al file utilizzato per il logging.
 */
static FILE * fileWriteLog;

/**
 * @brief Array dinamico utilizzato per registrare eventuali nomi da dover aggiungere o
 * escludere.
 *
 * La politica adottata dipende dalla variabile @see usePositiveLogic
 */
static DynArray loggedNames;

/**
 * @brief Array dinamico utilizzato per registrare eventuali livelli da dover aggiungere o
 * escludere.
 *
 * La politica adottata dipende dalla variabile @see usePositiveLogic
 */
static DynArray loggedLevels;

/**
 * @brief Specifica la logica da adottare per scegliere i livelli e i nomi
 * da registrare nel logging.
 *
 * Se true stampa tutti i nomi e livelli presenti negli array, se false stampa tutti
 * i nomi e livelli non presenti negli array.
 *
 * Per default si stampano tutti i nomi e livelli tranne quelli definiti.
 */
static bool usePositiveLogic = false;

/**
 * @brief Funzione utilizzata per confrontare due livelli, ovvero due interi.
 *
 * @param a Primo puntatore a intero da confrontare.
 * @param b Secondo puntatore a intero da confrontare.
 * @return 1 se maggiore il primo, 0 se uguali, -1 se maggiore b.
 */
int _defLog_cmpLevels(const void *a, const void *b){
	int *A = (int*)a; int *B = (int*) b;
	printf("confronto %d e %d \n",*A ,*B);
	if(*A>*B) return 1;
	else return (*A == *B)?0:-1;
}

/**
 * @brief Funzione utilizata per confrontare due nomi, ovvero due stringhe.
 *
 * Viene implementata per effettuare la conversione ma si può sostituire direttamente
 * con strcmp, questa funzione può essere utilizzata per monitorare le operazioni
 * del logger.
 *
 * @param a prima stringa.
 * @param b seconda stringa.
 * @return risultato della strcmp
 */
int _defLog_cmpNames(const void *a, const void *b){
	char*A = (char*)a; char*B = (char*)b;
	return strcmp(A,B);
}

/**
 * @brief Inizializza le strutture dati per gestire il logging.
 *
 */
void defLog_init(){
	/// - inizializza il mutex di controllo della scrittura sul file di log
	pthread_mutex_init(&writefcont,NULL);

	/// - crea ed apre il file per la scrittura dei messaggi
    char nameLogFile[25]; 			// stringa per il nome
	time_t timer;    time(&timer);  // inizializza il timer
    struct tm* tm_info;				// ricava le informazioni sul tempo locale
    tm_info = localtime(&timer);
    // crea la stringa secondo la formattazione specificata
    strftime(nameLogFile, 25, "%H-%m-%d-log.txt", tm_info);
	fileWriteLog = fopen(nameLogFile,"a");     // apre il file

	/// - inizializza gli array per i nomi e i livelli da loggare
	DynArray_init(&loggedLevels,_defLog_cmpLevels,sizeof(int));
	DynArray_init(&loggedNames,_defLog_cmpNames,DEFLOG_MAX_NAME);

	/// - assegna i valori all'interfaccia di logging
	Logging_setLoggingOut(defLog_outputFn);
	Logging_setLoggingLevelSelector(defLog_level);
	Logging_setLoggingNameSelector(defLog_name);
}

/**
 * @brief Libera la memoria e chiude i file descriptor aperti.
 */
void defLog_end(){
	/// - libera la memoria occupata dagli array
	DynArray_destroy(&loggedLevels);
	DynArray_destroy(&loggedNames);
	/// - chiude il file per la scrittura dei messaggi di logging.
	fclose(fileWriteLog);
}

/**
 * @brief Controlla se è necessario registrare i log per il livello passato.
 *
 * @param level livello da controllare.
 *
 * Effettua i seguenti passaggi:
 */
int defLog_level(int level){
	/// - individua la logica da adottare
	int logic = (usePositiveLogic)?true:false;
	/// - controlla che il livello fornito sia stato specificato da monitorare
	int *p = (int*) DynArray_get(&loggedLevels,&level);
	/// @return logic se il livello è presente
	if(p != NULL) return logic;
	/// @return -logic se il livello non è presente
	return !logic;
}

/**
 * @brief permette di indicare al logger se il nome è da stampare.
 *
 * @param name nome da controllare.
 *
 * Effettua i seguenti passaggi:
 */
int defLog_name(char *name){
	/// - individua la logica da adottare
	bool logic = (usePositiveLogic)?true:(false);
	/// - controlla che il nome fornito sia stato specificato da monitorare
	char *p = (char*) DynArray_get(&loggedNames,name);
	/// @return logic se il nome è presente
	if(p!=NULL) return logic;
	/// @return -logic se il nome non è presente
	return !logic;
}

/**
 * @brief Si occupa di scrivere i messaggi di log.
 *
 * @param msg messaggio da registrare.
 */
void defLog_outputFn(char *msg){
    time_t timer;
    char buffer[25];
    struct tm* tm_info;

    time(&timer);
    tm_info = localtime(&timer);

    strftime(buffer, 25, "%m:%d(%H:%M:%S)", tm_info);
	pthread_mutex_lock(&writefcont);
	fprintf(fileWriteLog,"%s log: %s\n",buffer,msg);
	pthread_mutex_unlock(&writefcont);
}

DynArray* getLevelsLogged(){return &loggedLevels;}

DynArray* getNamesLogged(){return &loggedNames;}

/**
 * @brief Setter della logica utilizzata.
 *
 * Passare true per monitorare tutti i livelli e i nomi negli array,
 * false altrimenti.
 *
 * @param logic logica da adottare.
 */
void setUsedLogic(bool logic){usePositiveLogic = logic;}

/**
 *
 * @return
 */
bool getUsedLogic(){return usePositiveLogic;}
